import { Component, cls, type StyledProps } from '@knno/jsx';
import SVGClose from '@mdi/svg/svg/close.svg';
import SVGMenu from '@mdi/svg/svg/menu.svg';
import { setElementVars } from '@vanilla-extract/dynamic';
import { hideScrollBar } from '../../theme/scrollbar.css';
import { vars } from '../../theme/vars.css';
import { isDescendant } from '../../tools/utils';
import { Icon } from '../icon/icon';
import { PopList, type PopListSelectHandler } from '../popList/popList';
import { DATA } from '../symbol';
import { tabPanelColorVar, tabStyle } from './tab.css';

type TabItem = {
	text?: string;
	icon?: string;
	disabled?: boolean;
	closable?: boolean;
	custom?: (() => Node) | Node;
};

export type TabDef = string | TabItem;

export type TabProps = StyledProps<Tab> & {
	autoHideTabs?: boolean;
	tabs?: TabDef[];
	active?: number;
	appearance?: 'default' | 'outline' | 'simple';
	tabPanelColor?: string;
	onTabActive?: (this: Tab, page: number) => void;
};

export class Tab extends Component<TabProps, Tab, HTMLDivElement> {
	protected render(): Node {
		return (
			<div
				class={cls(
					this.props.class,
					tabStyle,
					/^(outline|simple)$/.test(this.props.appearance ?? '') ? 'outline' : '',
					this.props.appearance === 'simple' ? 'simple' : '',
				)}
				style={this.props.style}
				ref={(d) => {
					setElementVars(d, {
						[tabPanelColorVar]: this.props.tabPanelColor ?? vars.color.windowBackground,
					});
				}}
			>
				{/** biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
				<div
					class={['head', hideScrollBar]}
					onWheel={(event) => {
						if (event.deltaY != 0) {
							const v = event.deltaY / Math.abs(event.deltaY);
							event.preventDefault();
							this.head.scrollBy(v * 20, 0);
						}
					}}
					onClick={(event) => {
						const target = event.target as HTMLElement;
						const elems = this.head.children;
						for (let i = 0; i < elems.length; i++) {
							const elem = elems[i];
							if (isDescendant([elem], target)) {
								if (target.nodeName.toLowerCase() === 'svg' && target.classList.contains('close')) {
									const newTabs = this.tabs.filter((_, idx) => idx != i);
									this.setTabs(newTabs);
									this.box.children[i]?.remove();
									event.stopPropagation();
								} else if (!elem.classList.contains('disable')) {
									this.setActive(i);
									this.call('onTabActive', i);
								}
								break;
							}
						}
					}}
				></div>
				<div class="body">{this.props.children}</div>
			</div>
		);
	}

	private get head(): HTMLDivElement {
		return this.el.querySelector(':scope>div.head') as HTMLDivElement;
	}

	/**
	 * Should use this property to add child pages
	 */
	get box(): HTMLDivElement {
		return this.el.querySelector(':scope>div.body') as HTMLDivElement;
	}

	private [DATA]: {
		arr: TabDef[];
		btn: HTMLButtonElement;
		act: number;
		pop: PopListSelectHandler | null;
		sheltered: (TabItem & { index: number })[];
		pad: number;
		autoHideTabs: boolean;
	};

	constructor(props: TabProps) {
		super(props);
		const btn = (
			<button
				type="button"
				class="menu"
				innerHTML={SVGMenu}
				onClick={(event) => {
					event.stopPropagation();
					this[DATA].pop = PopList.show(this[DATA].sheltered, {
						columnOption: { text: 'text' },
						refer: btn,
						disableField: 'disabled',
						popupPosition: 'bottom-right',
						width: 200,
						focus: true,
					})
						.onClose(() => {
							this[DATA].pop = null;
						})
						.onSelect((_, __, row) => {
							this.setActive(this[DATA].sheltered[row].index);
						});
				}}
			/>
		) as HTMLButtonElement;
		this[DATA] = {
			arr: props.tabs ?? [],
			btn,
			act: 0,
			pop: null,
			sheltered: [],
			pad: 0,
			autoHideTabs: props.autoHideTabs ?? false,
		};
		const observer = new MutationObserver(() => {
			this.showPage();
		});
		observer.observe(this.box, {
			subtree: false,
			childList: true,
			attributes: false,
			characterData: false,
		});
		const resizeObserver = new ResizeObserver(() => {
			this.renderHead();
		});
		resizeObserver.observe(this.el);
		this.setActive(props.active ?? 0);
	}

	private showPage() {
		if (this[DATA].act < 0) {
			this[DATA].act = 0;
		}
		if (this[DATA].act > this[DATA].arr.length - 1) {
			this[DATA].act = this[DATA].arr.length - 1;
		}
		Array.from(this.box.children).forEach((elem, idx) => {
			if (idx === this[DATA].act) {
				elem.classList.add('active');
			} else {
				elem.classList.remove('active');
			}
		});
	}

	private isTabSheltered(el: HTMLElement): boolean {
		return (
			el.offsetLeft + el.offsetWidth >
			this.head.clientWidth - (this[DATA].pad === 0 ? 0 : this[DATA].pad / 2)
		);
	}

	private showHideTabs() {
		if (this[DATA].autoHideTabs) {
			if (this[DATA].arr.length <= 1) {
				this.head.classList.add('hidden');
			} else {
				this.head.classList.remove('hidden');
			}
		} else {
			this.head.classList.remove('hidden');
		}
	}

	private renderHead() {
		const al = this[DATA].arr.length;
		const hl = this.head.children.length;
		for (let i = 0; i < al - hl; i++) {
			this.head.append(document.createElement('button'));
		}
		for (let i = 0; i < hl - al; i++) {
			if (this.head.lastChild && this.head.lastChild !== this[DATA].btn) {
				this.head.removeChild(this.head.lastChild);
			}
		}
		this[DATA].sheltered = [];
		this[DATA].arr.forEach((page, idx) => {
			let item: TabItem;
			if (typeof page === 'string') {
				item = { text: page };
			} else {
				item = page;
			}
			const label = this.head.children[idx] as HTMLButtonElement;
			label.innerHTML = item.icon ?? '';
			if (item.text || !item.custom) {
				label.append(item.text ?? idx + 1 + '');
			}
			if (typeof item.custom === 'function') {
				label.append(item.custom());
			} else if (item.custom instanceof Node) {
				label.append(item.custom);
			}
			if (item.closable) {
				label.append(<Icon svg={SVGClose} class="close" />);
			}
			if (idx === this[DATA].act) {
				label.classList.add('active');
				label.classList.remove('disable');
			} else {
				label.classList.remove('active');
				if (item.disabled) {
					label.classList.add('disable');
				} else {
					label.classList.remove('disable');
				}
			}
			const isSheltered = this.isTabSheltered(label);
			if (isSheltered) {
				this[DATA].sheltered.push({ ...item, index: idx });
			}
		});

		if (this[DATA].sheltered.length > 0) {
			this.head.classList.add('menu');
			this.head.append(this[DATA].btn);
			this[DATA].pad = this[DATA].btn.offsetWidth;
			if (this[DATA].pop) {
				this[DATA].pop.list.setRows(this[DATA].sheltered);
			}
		} else {
			this[DATA].pad = 0;
			this.head.classList.remove('menu');
			this[DATA].btn.remove();
			if (this[DATA].pop) {
				this[DATA].pop.close();
			}
		}
		this.showHideTabs();
	}

	get autoHideTabs(): boolean {
		return this[DATA].autoHideTabs;
	}
	setAutoHideTabs(value?: boolean): this | boolean {
		this[DATA].autoHideTabs = !!value;
		this.showHideTabs();
		return this;
	}

	get tabs(): TabDef[] {
		return this[DATA].arr;
	}
	setTabs(value: TabDef[]): this {
		this[DATA].arr = value;
		this.showPage();
		this.renderHead();
		return this;
	}

	get active(): number {
		return this[DATA].arr.length === 0 ? -1 : this[DATA].act;
	}

	setActive(value: number): this {
		this[DATA].act = value;
		this.showPage();
		this.renderHead();
		const activeBtn = this.head.querySelector<HTMLButtonElement>('button.active');
		if (activeBtn) {
			if (activeBtn.offsetLeft < this.head.scrollLeft) {
				this.head.scrollLeft = activeBtn.offsetLeft;
			}
			if (
				this.head.scrollLeft <
				activeBtn.offsetLeft + activeBtn.offsetWidth - (this.head.clientWidth - this[DATA].pad)
			) {
				this.head.scrollLeft =
					activeBtn.offsetLeft + activeBtn.offsetWidth - (this.head.clientWidth - this[DATA].pad);
			}
		}
		return this;
	}

	setAppearance(value: 'default' | 'outline' | 'simple'): this {
		this.el.classList.toggle('outline', /^(outline|simple)$/.test(value));
		this.el.classList.toggle('simple', value === 'simple');
		return this;
	}

	setPanelColor(value: string): this {
		setElementVars(this.el, {
			[tabPanelColorVar]: value,
		});
		return this;
	}
}
